package com.skynet.supervision.utils.functional;

import com.skynet.supervision.utils.CollectionExtUtils;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.*;


public final class FuncUtils {

    public static <T> Predicate<T> predicate(Predicate<T> t) {
        return t;
    }

    /**
     * wrap function to consumer
     *
     * @param fun input function
     * @param <T> func in param
     * @param <R> func out param
     * @return Consumer<X>
     */
    public static <T, R> Consumer<T> wrapFunction(@Nonnull Function<T, R> fun) {
        return (it) -> Objects.requireNonNull(fun).apply(it);
    }

    /**
     * 将 consumer 包装为 function
     * <p>
     * 注意,包装后的function返回值永远为空
     *
     * @param consumer 消费函数
     * @param <T>      入参类型
     * @param <R>      出参类型
     * @return Function<T, R>
     */
    public static <T, R> Function<T, R> wrapConsumer(@Nonnull Consumer<T> consumer) {
        return in -> {
            consumer.accept(in);
            return null;
        };
    }

    public static <T> Optional<T> makeOptional(@Nullable T value) {
        return Optional.ofNullable(value);
    }

    public static <T> Optional<T> makeOptional(@Nonnull Supplier<T> supplier) {
        return makeOptional(supplier.get());
    }

    public static <T extends Collection<?>> Predicate<T> collectionNotEmpty() {
        return CollectionExtUtils::notEmpty;
    }

    public static <T extends Map<?, ?>> Predicate<T> mapNotEmpty() {
        return CollectionExtUtils::notEmpty;
    }

    /**
     * 包装异常返回
     * **hack**
     *
     * @param t Throwable
     */
    @SuppressWarnings("unchecked")
    public static <T extends Throwable> void uncheckedThrow(Throwable t) throws T {
        throw (T) t;
    }


    public static <T> Predicate<T> rethrowPredicate(
            ThrowablePredicate<T, ? extends Throwable> function) {
        return function.toPredicate();
    }

    public static <T, R> Function<T, R> rethrowFunction(
            ThrowableFunction<T, R, ? extends Throwable> function) {
        return function.toFunction();
    }

    public static <T> Consumer<T> rethrowConsumer(
            ThrowableConsumer<T, ? extends Throwable> consumer) {
        return in -> {
            try {
                consumer.apply(in);
            } catch (Throwable e) {
                uncheckedThrow(e);
            }
        };
    }

    public static VoidConsumer rethrowVoidConsumer(
            ThrowableVoidConsumer<? extends Throwable> consumer) {
        return consumer.toVoidConsumer();
    }

    public static <T> Supplier<T> rethrowSupplier(ThrowableSupplier<T, ? extends Throwable> suppiler) {
        return suppiler.toSupplier();
    }

    public static <T> T TryAndReturn(@Nonnull ThrowableSupplier<T, ? super Throwable> supplier,
                                     Consumer<? super Throwable> errHandler) {
        try {
            return supplier.get();
        } catch (Throwable e) {
            if (errHandler != null) {
                errHandler.accept(e);
            } else {
                uncheckedThrow(e);
            }
        }
        return null;
    }


    public static <T> T TryAndReturn(@Nonnull ThrowableSupplier<T, ? super Throwable> supplier) {
        return TryAndReturn(supplier, null);
    }

    public static void Try(@Nonnull ThrowableVoidConsumer<?> consumer,
                           @Nonnull Consumer<? super Throwable> errHandler) {
        try {
            consumer.apply();
        } catch (Throwable e) {
            Objects.requireNonNull(errHandler).accept(e);
        }
    }

    public static <T> void rethrowExecute(T target,
                                          @Nonnull ThrowableConsumer<? super T, ?> consumer) {
        try {
            Objects.requireNonNull(consumer).apply(target);
        } catch (Throwable e) {
            uncheckedThrow(e);
        }
    }

    public static <T> void suppressExecute(T target,
                                           @Nonnull ThrowableConsumer<? super T, ?> consumer,
                                           Consumer<Throwable> errorHandler) {
        suppressExecute(consumer, errorHandler).accept(target);
    }


    public static <T> Consumer<? super T> suppressExecute(@Nonnull ThrowableConsumer<? super T, ?> consumer,
                                                          Consumer<Throwable> errorHandler) {
        return object -> {
            try {
                Objects.requireNonNull(consumer).apply(object);
            } catch (Throwable e) {
                Optional.ofNullable(errorHandler)
                        .ifPresent(it -> it.accept(e));
            }
        };
    }

    public static boolean boolRedirect(boolean b) {
        return b;
    }

    public static <T> BinaryOperator<T> mapConflictMerger() {
        return mapConflictMerger(true);
    }

    public static <T> BinaryOperator<T> mapConflictMerger(boolean keepRight) {
        return (l, r) -> keepRight ? r : l;
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object o) {
        return (T) o;
    }
}


